// (function(){   
//     history.replaceState(null,null,'');//最开始的状态，采用replace直接替换
//     $('#router').html('<p>显示内容区域</p>')
//     $('a').on('click',function(){
//         console.log(this.text)
//         var text = this.text;
//         $('#router').html('<p>'+ text +'</p>')
//         history.pushState(null,null,'#/'+text);
//     })  
//     // $('a').on('click',function(){
//     //     console.log(this.text)
//     //     var text = this.text
//     //     $("#router").html('<p>'+text+'</p>')
//     //     history.pushState(null,null,'#/'+text)
//     // })  
//   })()

// 状态版
// (function(){   
//     var count = [0,0,0]
//     $('#router').html('<p>首页</p>'+count[0]+'<p>新闻</p>'+count[1]+'<p>关于</p>'+count[2])
//     // history.replaceState(count,null,'');//最开始的状态，采用replace直接替换
 
//     for(var i = 0 ; i<$('a').length; i++){
//         $('a')[i].index = i
//         $('a').eq(i).on('click',function(){
//             // console.log(this.index);
//             var index = this.index;
//             count[index]++;
//             $('#router').html('<p>首页</p>'+count[0]+'<p>新闻</p>'+count[1]+'<p>关于</p>'+count[2])
//             // console.log(count)
//             history.pushState(count,null,'#/count'+count[index]);//之后的状态，需要进行保存
//         })
//     }
//     //监听history其他api导致地址栏url改变事件
//     window.addEventListener('popstate',function(e){
//         console.log(e.state);
//         var state = e.state;
//         $('#router').html('<p>首页</p>'+state[0]+'<p>新闻</p>'+state[1]+'<p>关于</p>'+state[2])
 
//     })
//  })()
  
// (function(){   

//     var url = '内容展示';

//     history.replaceState(url,null,'');//最开始的状态，采用replace直接替换
//     $('#router').html('<p>'+url+'</p>')

//     $('a').on('click',function(){
//         console.log(this.text)
//         url = this.text;

//         $('#router').html('<p>'+ url +'</p>')
//         history.pushState(url,null,'#/'+url);
// //     })
//     window.addEventListener('popstate',function(e){
//         console.log(e.state);
//         url = e.state
//         $('#router').html('<p>'+ url +'</p>')

//      });     
// })()

window.location.hash = 'hash字符串'; // 用于设置 hash 值

let hash = window.location.hash; // 获取当前 hash 值

// 监听hash变化，点击浏览器的前进后退会触发
window.addEventListener('hashchange', function(event){ 
    let newURL = event.newURL; // hash 改变后的新 url
    let oldURL = event.oldURL; // hash 改变前的旧 url
},false)

class HashRouter{
    constructor(){
        //用于存储不同hash值对应的回调函数
        this.routers = {};
        window.addEventListener('hashchange',this.load.bind(this),false)
    }
    //用于注册每个视图
    register(hash,callback = function(){}){
        this.routers[hash] = callback;
    }
    //用于注册首页
    registerIndex(callback = function(){}){
        this.routers['index'] = callback;
    }
    //用于调用不同视图的回调函数
    load(){
        let hash = location.hash.slice(1),
            handler;
        //没有hash 默认为首页
        if(!hash){
            handler = this.routers.index;
        }else{
            handler = this.routers[hash];
        }
        //执行注册的回调函数
        handler.call(this);
    }
}

let router = new HashRouter();
let container = document.getElementById('container');

//注册首页回调函数
router.registerIndex(()=> container.innerHTML = '我是首页');

//注册其他视图回到函数
router.register('/page1',()=> container.innerHTML = '我是page1');
router.register('/page2',()=> container.innerHTML = '我是page2');
router.register('/page3',()=> container.innerHTML = '我是page3');

//加载视图
router.load();

window.addEventListener("error",function(e){
    if (e.target !== window) { // 避免重复上报
        console.log({
            url: window.location.href, // 引用资源地址
            srcUrl: e.target.src, // 资源加载出错地址
        })
      } 
},true)

Promise.reject('promise error');
new Promise((resolve, reject) => {
  reject('promise error');
});
new Promise((resolve) => {
  resolve();
}).then(() => {
  throw 'promise error';
});


window.addEventListener('unhandlerejection',(e)=>{
  //。。。
})

import { SourceMapConsumer } from 'source-map';

// 必须初始化
SourceMapConsumer.initialize({
  'lib/mappings.wasm': 'https://unpkg.com/source-map@0.7.3/lib/mappings.wasm',
});

/**
 * 根据sourceMap文件解析源代码
 * @param {String} rawSourceMap sourceMap文件
 * @param {Number} line 压缩代码报错行
 * @param {Number} column 压缩代码报错列
 * @param {Number} offset 设置返回临近行数
 * @returns {Promise<{context: string, originLine: number | null, source: string | null}>}
 * context：源码错误行和上下附近的 offset 行，originLine：源码报错行，source：源码文件名
 */
export const sourceMapDeal = async (rawSourceMap, line, column, offset) => {
  // 通过sourceMap库转换为sourceMapConsumer对象
  const consumer = await new SourceMapConsumer(rawSourceMap);
  // 传入要查找的行列数，查找到压缩前的源文件及行列数
  const sm = consumer.originalPositionFor({
    line, // 压缩后的行数
    column, // 压缩后的列数
  });
  // 压缩前的所有源文件列表
  const { sources } = consumer;
  // 根据查到的source，到源文件列表中查找索引位置
  const smIndex = sources.indexOf(sm.source);
  // 到源码列表中查到源代码
  const smContent = consumer.sourcesContent[smIndex];
  // 将源代码串按"行结束标记"拆分为数组形式
  const rawLines = smContent.split(/\r?\n/g);
  let begin = sm.line - offset;
  const end = sm.line + offset + 1;
  begin = begin < 0 ? 0 : begin;
  const context = rawLines.slice(begin, end).join('\n');
  // 记得销毁
  consumer.destroy();
  return {
    context,
    originLine: sm.line + 1, // line 是从 0 开始数，所以 +1
    source: sm.source,
  }
};

class Root extends React.PureComponent {
  state = { hasError: false }
  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasError: true };
  }
  // 可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误，并且，它会渲染出备用 UI
  // 两者都可以做错误边界，但同时存在时只响应其中一个，优先响应getDerivedStateFromError。
  componentDidCatch(error) {
    this.setState({ hasError: true });
    console.log('errorcatch', error); // 上报Error
  }
  render() {
    const { hasError } = this.state;
    if (hasError) {
      return <div>有错误</div>
    }
    return (
      <Provider store={store}>
        <Router>
          <Route path="/" component={Layout} />
        </Router>
      </Provider>
    );
  }

}


const rAF = (() => {
  const SIXTY_TIMES = 60;
  const requestAnimationFrame = window.requestAnimationFrame;
  if (requestAnimationFrame) {
    return (cb) => {
      const timer = requestAnimationFrame(() => {
        cb();
        window.cancelAnimationFrame(timer);
      });
    };
  // requestAnimationFrame 兼容实现
})();

function stuck() {
  const stucks = [];
  const startTime = Date.now();
  const loop = (startCountTime = Date.now(), lastFrameCount = 0) => {
    const now = Date.now();
    // 每一帧进来，计数一次
    const nowFrameCount = lastFrameCount + 1;
    // 大于等于一秒钟为一个周期；比如如果是正常的fps： 那当第61次时，即1017毫秒，这里就满足 16.6毫秒去刷新一次
    if (now > ONE_SECOND + startCountTime) {
      // 计算一秒钟的fps： 当前计数总次数 / 经过的时长；
      const timeInterval = (now - startCountTime) / ONE_SECOND;
      const fps = Math.round(nowFrameCount / timeInterval);
      if (fps > 30) { // fps 小于30 判断为卡顿
        stucks.pop();
      } else {
        stucks.push(fps);
      }
      // 连续三次小于30 上报卡顿（还有一种特殊情况，前面2次卡顿，第三次不卡，接着再连续两次卡顿，也满足）
      if (stucks.length === 3) {
          console.log(new Error(`Page Stuck captured: ${location.href} ${stucks.join(',')} ${now - startTime}ms`));
        // 清空采集到的卡顿数据
        stucks.length = 0;
      }
      // 即休息一个周期（我这里定义的是一分钟），重新开启采样
      const timer = setTimeout(() => {
        loop();
        clearTimeout(timer);
      }, 60 * 1000);
      return;
    }
    rAF(() => loop(startCountTime, nowFrameCount));
  };
  loop();
};

